package com.frank.practice.service.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.frank.auth.entity.AuthUserDto;
import com.frank.common.enums.IsDeletedFlagEnum;
import com.frank.common.utils.LoginUtil;
import com.frank.practice.api.req.*;
import com.frank.practice.api.vo.*;
import com.frank.practice.entity.*;
import com.frank.practice.entity.dto.SubjectAnswerOptionDto;
import com.frank.practice.entity.dto.SubjectDetailDto;
import com.frank.practice.entity.po.PracticeRankPo;
import com.frank.practice.enums.AnswerStatusEnum;
import com.frank.practice.enums.CompleteStatusEnum;
import com.frank.practice.enums.SubjectInfoTypeEnum;
import com.frank.practice.mapper.*;
import com.frank.practice.rpc.UserRpc;
import com.frank.practice.service.PracticeDetailService;
import com.frank.practice.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author Frank Zou
 * @version v0.0.1
 * @date 2025/6/19
 * @description
 */
@Service
public class PracticeDetailServiceImpl implements PracticeDetailService {

    private final PracticeDetailMapper practiceDetailMapper;

    private final PracticeInfoMapper practiceInfoMapper;

    private final SubjectInfoMapper subjectInfoMapper;

    private final SubjectRadioMapper subjectRadioMapper;

    private final SubjectMultipleMapper subjectMultipleMapper;

    private final SubjectJudgeMapper subjectJudgeMapper;

    private final PracticeSetDetailMapper practiceSetDetailMapper;

    private final PracticeSetMapper practiceSetMapper;

    private final SubjectLabelMapper subjectLabelMapper;

    private final UserRpc userRpc;

    @Autowired
    public PracticeDetailServiceImpl(
            PracticeDetailMapper practiceDetailMapper,
            PracticeInfoMapper practiceInfoMapper,
            SubjectInfoMapper subjectInfoMapper,
            SubjectRadioMapper subjectRadioMapper,
            SubjectMultipleMapper subjectMultipleMapper,
            SubjectJudgeMapper subjectJudgeMapper,
            PracticeSetDetailMapper practiceSetDetailMapper,
            PracticeSetMapper practiceSetMapper,
            SubjectLabelMapper subjectLabelMapper,
            UserRpc userRpc) {
        this.practiceDetailMapper = practiceDetailMapper;
        this.practiceInfoMapper = practiceInfoMapper;
        this.subjectInfoMapper = subjectInfoMapper;
        this.subjectRadioMapper = subjectRadioMapper;
        this.subjectMultipleMapper = subjectMultipleMapper;
        this.subjectJudgeMapper = subjectJudgeMapper;
        this.practiceSetDetailMapper = practiceSetDetailMapper;
        this.practiceSetMapper = practiceSetMapper;
        this.subjectLabelMapper = subjectLabelMapper;
        this.userRpc = userRpc;
    }

    @Override
    public Boolean submitSubject(SubmitSubjectDetailReq req) {
        String timeUse = req.getTimeUse();
        if (Objects.equals("0", timeUse)) {
            timeUse = "00:00:00";
        }
        String hour = timeUse.substring(0, 2);
        String minute = timeUse.substring(2, 4);
        String second = timeUse.substring(4, 6);
        PracticeInfo practiceInfo = new PracticeInfo();
        practiceInfo.setId(req.getPracticeId());
        practiceInfo.setTimeUse(hour + ":" + minute + ":" + second);
        practiceInfo.setSubmitTime(new Date());
        practiceInfoMapper.updateById(practiceInfo);
        PracticeDetail practiceDetail = new PracticeDetail();
        practiceDetail.setPracticeId(req.getPracticeId());
        practiceDetail.setSubjectId(req.getSubjectId());
        practiceDetail.setSubjectType(req.getSubjectType());
        String answerContent = "";
        if (CollectionUtils.isNotEmpty(req.getAnswerContents())) {
            List<Integer> answerContents = req.getAnswerContents();
            Collections.sort(answerContents);
            answerContent = StringUtils.join(answerContents, ",");
        }
        practiceDetail.setAnswerContent(answerContent);
        SubjectDetailDto subjectDetail = getSubjectDetail(req.getSubjectId());
        StringBuffer correctAnswer = new StringBuffer();
        if (SubjectInfoTypeEnum.JUDGE.getCode().equals(req.getSubjectType())) {
            Integer isCorrect = subjectDetail.getIsCorrect();
            correctAnswer.append(isCorrect);
        } else {
            subjectDetail.getOptionList().forEach(option -> {
                if (Objects.equals(option.getIsCorrect(), AnswerStatusEnum.CORRECT.getStatus())) {
                    correctAnswer.append(option.getOptionType()).append(",");
                }
            });
            if (correctAnswer.length() > 0) {
                correctAnswer.deleteCharAt(correctAnswer.length() - 1);
            }
        }
        if (Objects.equals(correctAnswer.toString(), answerContent)) {
            practiceDetail.setAnswerStatus(AnswerStatusEnum.CORRECT.getStatus());
        } else {
            practiceDetail.setAnswerStatus(AnswerStatusEnum.UN_CORRECT.getStatus());
        }
        practiceDetail.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practiceDetail.setCreatedBy(LoginUtil.getLoginId());
        practiceDetail.setCreatedTime(new Date());
        LambdaQueryWrapper<PracticeDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(PracticeDetail::getSubjectId, req.getSubjectId())
                .eq(PracticeDetail::getPracticeId, req.getPracticeId())
                .eq(PracticeDetail::getCreatedBy, LoginUtil.getLoginId());
        PracticeDetail existPracticeDetail = practiceDetailMapper.selectOne(queryWrapper);
        // 更新或插入
        int affectedRows;
        if (Objects.nonNull(existPracticeDetail)) {
            practiceDetail.setId(existPracticeDetail.getId());
            affectedRows = practiceDetailMapper.updateById(practiceDetail);
        } else {
            affectedRows = practiceDetailMapper.insert(practiceDetail);
        }
        return affectedRows > 0;
    }

    /**
     * TODO 2025 年 6 月 19 日 16:56:14 获取试题的详情以及答案列表
     *
     * @param subjectId 试题id
     * @return 试题详情以及答案列表
     */
    public SubjectDetailDto getSubjectDetail(Long subjectId) {
        SubjectDetailDto result = new SubjectDetailDto();
        SubjectInfo subjectInfo = subjectInfoMapper.selectById(subjectId);
        List<SubjectAnswerOptionDto> answerOptionList = new LinkedList<>();
        if (SubjectInfoTypeEnum.RADIO.getCode().equals(subjectInfo.getSubjectType())) {
            // 单选题
            List<SubjectRadio> subjectRadios = subjectRadioMapper.queryBySubjectId(subjectId);
            subjectRadios.forEach(subjectRadio -> {
                SubjectAnswerOptionDto option = new SubjectAnswerOptionDto();
                option.setOptionType(subjectRadio.getOptionType());
                option.setOptionContent(subjectRadio.getOptionContent());
                option.setIsCorrect(subjectRadio.getIsCorrect());
                answerOptionList.add(option);
            });
        } else if (SubjectInfoTypeEnum.MULTIPLE.getCode().equals(subjectInfo.getSubjectType())) {
            // 多选题
            List<SubjectMultiple> subjectMultiples = subjectMultipleMapper.queryBySubjectId(subjectId);
            subjectMultiples.forEach(subjectMultiple -> {
                SubjectAnswerOptionDto option = new SubjectAnswerOptionDto();
                option.setOptionType(subjectMultiple.getOptionType());
                option.setOptionContent(subjectMultiple.getOptionContent());
                option.setIsCorrect(subjectMultiple.getIsCorrect());
                answerOptionList.add(option);
            });
        } else if (SubjectInfoTypeEnum.JUDGE.getCode().equals(subjectInfo.getSubjectType())) {
            // 判断题
            List<SubjectJudge> subjectJudges = subjectJudgeMapper.queryBySubjectId(subjectId);
            subjectJudges.forEach(subjectJudge -> {
                SubjectAnswerOptionDto option = new SubjectAnswerOptionDto();
                option.setIsCorrect(subjectJudge.getIsCorrect());
                answerOptionList.add(option);
            });
        }
        result.setOptionList(answerOptionList);
        result.setSubjectParse(subjectInfo.getSubjectParse());
        result.setSubjectName(subjectInfo.getSubjectName());
        return result;
    }


    @Override
    public Boolean submitPractice(SubmitPracticeDetailReq req) {
        PracticeInfo practiceInfo = new PracticeInfo();
        String timeUse = req.getTimeUse();
        String hour = timeUse.substring(0, 2);
        String minute = timeUse.substring(2, 4);
        String second = timeUse.substring(4, 6);
        // 保存试题信息
        practiceInfo.setSetId(req.getSetId());
        practiceInfo.setTimeUse(hour + ":" + minute + ":" + second);
        practiceInfo.setSubmitTime(DateUtils.parseStrToDate(req.getSubmitTime()));
        practiceInfo.setCompleteStatus(CompleteStatusEnum.COMPLETE.getCode());
        practiceInfo.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practiceInfo.setCreatedBy(LoginUtil.getLoginId());
        practiceInfo.setCreatedTime(new Date());
        // 获取试题的正确率
        int correctCount = practiceDetailMapper.countByPracticeIdAndAnswerStatus(req.getPracticeId(), AnswerStatusEnum.CORRECT.getStatus());
        List<PracticeSetDetail> practiceSetDetails = practiceSetDetailMapper.listBySetId(req.getSetId());
        int totalCount = practiceSetDetails.size();
        // 计算正确率
        BigDecimal correctRate = new BigDecimal(correctCount).divide(new BigDecimal(totalCount), 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100.00"));
        practiceInfo.setCorrectRate(correctRate);
        PracticeInfo existPracticeInfo = practiceInfoMapper.selectById(req.getPracticeId());
        if (Objects.nonNull(existPracticeInfo)) {
            practiceInfo.setId(existPracticeInfo.getId());
            practiceInfoMapper.updateById(practiceInfo);
        } else {
            practiceInfoMapper.insert(practiceInfo);
        }
        // 增加套圈的热度
        practiceSetMapper.updateHeat(req.getSetId());
        // 补充剩余题目的记录
        List<PracticeDetail> practiceDetails = practiceDetailMapper.listByPracticeId(req.getPracticeId());
        List<Long> practiceDetailSubjectIdList = practiceDetails.stream().map(PracticeDetail::getSubjectId).collect(Collectors.toList());
        List<PracticeSetDetail> minusList = practiceSetDetails.stream()
                .filter(practiceSetDetail -> !practiceDetailSubjectIdList.contains(practiceSetDetail.getSubjectId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(minusList)) {
            List<PracticeDetail> collect = minusList.stream().map(item -> {
                PracticeDetail practiceDetail = new PracticeDetail();
                practiceDetail.setPracticeId(req.getPracticeId());
                practiceDetail.setSubjectType(item.getSubjectType());
                practiceDetail.setSubjectId(item.getSubjectId());
                practiceDetail.setAnswerStatus(AnswerStatusEnum.UN_CORRECT.getStatus());
                practiceDetail.setAnswerContent("");
                practiceDetail.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
                practiceDetail.setCreatedBy(LoginUtil.getLoginId());
                practiceDetail.setCreatedTime(new Date());
                return practiceDetail;
            }).collect(Collectors.toList());
            practiceDetailMapper.saveBatch(collect);
        }
        return true;
    }


    @Override
    public List<ScoreDetailVo> getScoreDetail(GetScoreDetailReq req) {
        Long practiceId = req.getPracticeId();
        List<ScoreDetailVo> result = new LinkedList<>();
        List<PracticeDetail> practiceDetails = practiceDetailMapper.findByPracticeId(practiceId);
        practiceDetails.forEach(practiceDetail -> {
            ScoreDetailVo scoreDetailVo = new ScoreDetailVo();
            scoreDetailVo.setSubjectId(practiceDetail.getSubjectId());
            scoreDetailVo.setSubjectType(practiceDetail.getSubjectType());
            scoreDetailVo.setIsCorrect(practiceDetail.getAnswerStatus());
            result.add(scoreDetailVo);
        });
        return result;
    }

    @Override
    public SubjectDetailVo getSubjectDetail(GetSubjectDetailReq req) {
        SubjectDetailVo result = new SubjectDetailVo();
        SubjectDetailDto subjectDetail = getSubjectDetail(req.getSubjectId());
        List<PracticeSubjectOptionVo> optionList = new LinkedList<>();
        List<Integer> correctAnswer = new LinkedList<>();
        if (CollectionUtils.isNotEmpty(optionList)) {
            subjectDetail.getOptionList().forEach(option -> {
                PracticeSubjectOptionVo optionVo = new PracticeSubjectOptionVo();
                optionVo.setOptionType(option.getOptionType());
                optionVo.setOptionContent(option.getOptionContent());
                optionVo.setIsCorrect(option.getIsCorrect());
                optionList.add(optionVo);
                if (option.getIsCorrect() == AnswerStatusEnum.CORRECT.getStatus()) {
                    correctAnswer.add(option.getOptionType());
                }
            });
        }
        if (SubjectInfoTypeEnum.JUDGE.getCode().equals(req.getSubjectType())) {
            // 判断题
            Integer isCorrect = subjectDetail.getIsCorrect();
            // 添加正确选项
            PracticeSubjectOptionVo correctOption = new PracticeSubjectOptionVo();
            correctOption.setOptionType(AnswerStatusEnum.CORRECT.getStatus());
            correctOption.setOptionContent(AnswerStatusEnum.CORRECT.getDesc());
            correctOption.setIsCorrect(isCorrect == 1 ? 1 : 0);
            optionList.add(correctOption);
            // 添加错误选项
            PracticeSubjectOptionVo errorOption = new PracticeSubjectOptionVo();
            errorOption.setOptionType(AnswerStatusEnum.UN_CORRECT.getStatus());
            errorOption.setOptionContent(AnswerStatusEnum.UN_CORRECT.getDesc());
            errorOption.setIsCorrect(isCorrect == 0 ? 1 : 0);
            optionList.add(errorOption);
        }
        result.setOptionList(optionList);
        result.setSubjectParse(subjectDetail.getSubjectParse());
        result.setSubjectName(subjectDetail.getSubjectName());
        result.setCorrectAnswer(correctAnswer);
        // 自己的答题答案
        List<Integer> respondAnswer = new LinkedList<>();
        PracticeDetail practiceDetail = practiceDetailMapper.selectAnswer(req.getPracticeId(), req.getSubjectId());
        String answerContent = practiceDetail.getAnswerContent();
        if (StringUtils.isNotBlank(answerContent)) {
            String[] split = answerContent.split(",");
            for (String s : split) {
                respondAnswer.add(Integer.valueOf(s));
            }
        }
        result.setRespondAnswer(respondAnswer);
        // 填充标签信息
        List<String> labelNameList = subjectLabelMapper.findLabelNameBySubjectId(req.getSubjectId());
        result.setLabelNames(labelNameList);
        return result;
    }


    @Override
    public ReportVo getReport(Long practiceId) {
        ReportVo result = new ReportVo();
        PracticeInfo practiceInfo = practiceInfoMapper.selectById(practiceId);
        PracticeSet practiceSet = practiceSetMapper.selectById(practiceInfo.getSetId());
        result.setTitle(practiceSet.getSetName());
        List<PracticeDetail> practiceDetailList = practiceDetailMapper.findByPracticeId(practiceId);
        if (CollectionUtils.isEmpty(practiceDetailList)) {
            return null;
        }
        int totalCount = practiceDetailList.size();
        List<PracticeDetail> correctList = practiceDetailList.stream().filter(e ->
                Objects.equals(e.getAnswerStatus(), AnswerStatusEnum.CORRECT.getStatus())).collect(Collectors.toList());
        result.setCorrectSubjectCount(correctList.size() + "/" + totalCount);
        List<ReportSkillVo> skillList = new LinkedList<>();
        Map<Long, Integer> totalMap = getSubjectLabelMap(practiceDetailList);
        Map<Long, Integer> correctMap = getSubjectLabelMap(correctList);
        totalMap.forEach((key, value) -> {
            ReportSkillVo skillVo = new ReportSkillVo();
            SubjectLabel subjectLabel = subjectLabelMapper.selectById(key);
            Integer correctCount = correctMap.get(key);
            if (Objects.isNull(correctCount)) {
                correctCount = 0;
            }
            skillVo.setName(subjectLabel.getLabelName());
            BigDecimal rate = BigDecimal.ZERO;
            if (!Objects.equals(value, 0)) {
                rate = BigDecimal.valueOf(correctCount)
                        .divide(BigDecimal.valueOf(totalMap.get(key)), 2, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100));
            }
            skillVo.setStar(rate);
            skillList.add(skillVo);
        });
        result.setSkill(skillList);
        return result;
    }

    /**
     * 根据试题 ID 获取标签信息以及标签对应的数量
     *
     * @param practiceDetailList 试题列表
     * @return 标签信息以及标签对应的数量
     */
    private Map<Long, Integer> getSubjectLabelMap(List<PracticeDetail> practiceDetailList) {
        if (CollectionUtils.isNotEmpty(practiceDetailList)) {
            return Collections.emptyMap();
        }
        Map<Long, Integer> result = new HashMap<>();
        practiceDetailList.forEach(detail -> {
            Long subjectId = detail.getSubjectId();
            List<Long> labelIdList = subjectLabelMapper.findLabelIdsBySubjectId(subjectId);
            labelIdList.forEach(labelId -> {
                if (result.containsKey(labelId)) {
                    result.put(labelId, result.get(labelId) + 1);
                } else {
                    result.put(labelId, 1);
                }
            });
        });
        return result;
    }

    @Override
    public List<RankVo> getPracticeRankList() {
        List<PracticeRankPo> practiceRankList = practiceDetailMapper.getPracticeCount();
        if (CollectionUtils.isEmpty(practiceRankList)) {
            return Collections.emptyList();
        }
        return practiceRankList.stream().map(item -> {
            RankVo rankVo = new RankVo();
            rankVo.setCount(item.getCount());
            AuthUserDto userInfo = userRpc.getUserInfo(item.getCreatedBy());
            rankVo.setName(StringUtils.isNotBlank(userInfo.getNickName()) ? userInfo.getNickName() : userInfo.getUserName());
            rankVo.setAvatar(userInfo.getAvatar());
            return rankVo;
        }).collect(Collectors.toList());
    }

    @Override
    public Boolean giveUp(GiveUpPracticeReq req) {
        practiceInfoMapper.deleteById(req.getPracticeId());
        practiceDetailMapper.deleteByPracticeId(req.getPracticeId());
        return true;
    }
}
